{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2. 第二章 - 如何训练一个神经网络（简单版）\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "目录：\n",
    "\n",
    "第一部分 神经网络\n",
    "* 2.0 神经网络简介  \n",
    "\n",
    "第二部分 线性回归\n",
    "* 2.1 加载数据\n",
    "* 2.2 定义模型\n",
    "* 2.3 定义损失函数\n",
    "* 2.4 选择优化器\n",
    "* 2.5 训练模型并验证\n",
    "* 2.6 附：可视化验证"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.0 神经网络简介"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "以监督学习为例，我们训练一个神经网络，目标是：给定输入数据及其正确的标签，将输入数据传入我们的神经网络中，通过神经网络的复杂计算，得到对应的输出值，使这个输出值尽可能地与正确标签相符。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "为了实现上述目标，我们以这样一种简单的方式理解神经网络的训练流程：  \n",
    "* 首先，我们拿到含有正确标签的数据（获取数据集）；  \n",
    "* 然后，我们定义一系列含有待定参数的计算（定义神经网络，其参数就是我们的学习目标）；  \n",
    "* 接着，我们将数据按照第二步定义好的规则，计算得到对应的输出值（前向传播）；  \n",
    "* 随后，我们根据一定规则，比较输出值与正确标签的差异（计算损失函数）；  \n",
    "* 最后，我们根据输出值与正确标签的差异大小，反观我们当前计算中所使用的各个参数，对参数进行优化更新（反向传播以及参数优化）；  \n",
    "以此反复，优化我们的计算，最终寻找到该计算（神经网络）的最佳参数。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对应地，在 Jittor (计图) 中，我们可以按如下顺序训练一个神经网络：\n",
    "1. 加载数据\n",
    "2. 定义模型\n",
    "3. 定义损失函数\n",
    "4. 选择优化器\n",
    "5. 训练模型（并验证）\n",
    "\n",
    "现在，我们按照上述流程，实现一个简单的神经网络吧！（线性回归问题）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\u001b[38;5;2m[i 0202 22:58:27.866990 76 compiler.py:847] Jittor(1.2.2.27) src: /home/llt/.local/lib/python3.7/site-packages/jittor\u001b[m\n",
      "\u001b[38;5;2m[i 0202 22:58:27.869186 76 compiler.py:848] g++ at /usr/bin/g++\u001b[m\n",
      "\u001b[38;5;2m[i 0202 22:58:27.870261 76 compiler.py:849] cache_path: /home/llt/.cache/jittor/default/g++\u001b[m\n",
      "\u001b[38;5;2m[i 0202 22:58:27.886880 76 __init__.py:257] Found /usr/local/cuda/bin/nvcc(10.2.89) at /usr/local/cuda/bin/nvcc.\u001b[m\n",
      "\u001b[38;5;2m[i 0202 22:58:27.959711 76 __init__.py:257] Found gdb(8.1.0) at /usr/bin/gdb.\u001b[m\n",
      "\u001b[38;5;2m[i 0202 22:58:27.976226 76 __init__.py:257] Found addr2line(2.30) at /usr/bin/addr2line.\u001b[m\n",
      "\u001b[38;5;2m[i 0202 22:58:28.026845 76 compiler.py:889] pybind_include: -I/usr/include/python3.7m -I/usr/local/lib/python3.7/dist-packages/pybind11/include\u001b[m\n",
      "\u001b[38;5;2m[i 0202 22:58:28.051528 76 compiler.py:891] extension_suffix: .cpython-37m-x86_64-linux-gnu.so\u001b[m\n",
      "\u001b[38;5;2m[i 0202 22:58:28.245442 76 __init__.py:169] Total mem: 62.78GB, using 16 procs for compiling.\u001b[m\n",
      "\u001b[38;5;2m[i 0202 22:58:30.601735 76 jit_compiler.cc:21] Load cc_path: /usr/bin/g++\u001b[m\n",
      "\u001b[38;5;2m[i 0202 22:58:30.801683 76 init.cc:54] Found cuda archs: [75,]\u001b[m\n",
      "\u001b[38;5;2m[i 0202 22:58:30.994661 76 __init__.py:257] Found mpicc(2.1.1) at /usr/bin/mpicc.\u001b[m\n",
      "\u001b[38;5;2m[i 0202 22:58:31.058753 76 compiler.py:654] handle pyjt_include/home/llt/.local/lib/python3.7/site-packages/jittor/extern/mpi/inc/mpi_warper.h\u001b[m\n",
      "\u001b[38;5;2m[i 0202 22:58:31.108971 76 compile_extern.py:287] Downloading nccl...\u001b[m\n",
      "\u001b[38;5;2m[i 0202 22:58:31.187842 76 compile_extern.py:16] found /usr/local/cuda/include/cublas.h\u001b[m\n",
      "\u001b[38;5;2m[i 0202 22:58:31.189916 76 compile_extern.py:16] found /usr/lib/x86_64-linux-gnu/libcublas.so\u001b[m\n",
      "\u001b[38;5;2m[i 0202 22:58:31.780064 76 compile_extern.py:16] found /usr/local/cuda/include/cudnn.h\u001b[m\n",
      "\u001b[38;5;2m[i 0202 22:58:31.781034 76 compile_extern.py:16] found /usr/local/cuda/lib64/libcudnn.so\u001b[m\n",
      "\u001b[38;5;2m[i 0202 22:58:31.809573 76 compiler.py:654] handle pyjt_include/home/llt/.local/lib/python3.7/site-packages/jittor/extern/cuda/cudnn/inc/cudnn_warper.h\u001b[m\n",
      "\u001b[38;5;2m[i 0202 22:58:33.621312 76 compile_extern.py:16] found /usr/local/cuda/include/curand.h\u001b[m\n",
      "\u001b[38;5;2m[i 0202 22:58:33.622719 76 compile_extern.py:16] found /usr/local/cuda/lib64/libcurand.so\u001b[m\n"
     ]
    }
   ],
   "source": [
    "# 加载计图\n",
    "import jittor as jt\n",
    "\n",
    "# 开启 GPU 加速\n",
    "# jt.flags.use_cuda = 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 任务：线性回归问题\n",
    "\n",
    "任务描述如下：  \n",
    "* 已知 x 和 y 具有一定的线性关系。\n",
    "* 给定 x 的值，用模型预测 y 的值。 \n",
    "\n",
    "解决步骤如下：\n",
    "* 首先，我们会随机生成一些具有线性关系的数据点 $(x, y)$，当作我们的数据集；  \n",
    "* 然后，定义我们的计算模型为 $y=a + b \\cdot x $；  \n",
    "* 接着，我们用计图的内置函数，选择我们的损失函数和参数优化器；  \n",
    "* 最后，我们将完成训练模型的主代码块以及验证部分的代码。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.1 加载数据"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先，我们要准备好实验的数据集。 \n",
    "\n",
    "在这个线性回归问题中，我们会随机生成 100 个数据点 $(x, y)$。其中，x 和 y 潜在的线性关系为 $y=a + b \\cdot x $ （我们会为 $y$ 设置一定的噪音 ）。这里，$a$ 和 $b$ 为我们模型将要学习的参数。**我们先手动设置 $a = 1$，$b = 2$ 来生成数据集。然后，我们用这样一个数据集训练我们的模型，看看模型是否有能力学习到这两个参数值。**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "# 设定种子，保持结果的可复制性。\n",
    "np.random.seed(2021)  \n",
    "\n",
    "# 初始化 100 个数据点。其中，x 为输入数据，y 为正确的标签值（通过 x 预测的值）。\n",
    "x = np.random.rand(100).reshape(100,1)\n",
    "y = 1 + 2 * x + 0.1 * np.random.randn(100,1)             # 线性关系设置为 a = 1, b = 2,并利用正态分布，给 y 的值设定噪音\n",
    "\n",
    "# 将我们的数据点，切分为训练集和验证集（先随机切分索引，再根据索引切分数据集）\n",
    "index = np.arange(100)                                   # 生成 100 个索引值\n",
    "np.random.shuffle(index)                                 # 将索引随机排序\n",
    "train_index = index[:80]                                 # 训练集数据对应的索引\n",
    "val_index = index[-20:]                                  # 验证集数据对应的索引\n",
    "\n",
    "# 根据索引，切分训练集和验证集\n",
    "x_train, y_train = x[train_index], y[train_index]\n",
    "x_val, y_val = x[val_index], y[val_index]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "将数据集准备好后，我们通过 Matplotlib 查看一下数据点的分布"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.collections.PathCollection at 0x7f31b3c92d50>"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "fig, axs = plt.subplots(nrows = 1, ncols = 2)\n",
    "axs[0].scatter(x_train, y_train)                         # 展示训练集上的数据点\n",
    "axs[1].scatter(x_val, y_val)                             # 展示验证集上的数据点"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最后，我们将数据点加载到 Jittor 中，转化为 Jittor 可操作的 Var 类型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 将 NumPy 数组转化成 Var\n",
    "x_train_var = jt.array(x_train)\n",
    "y_train_var = jt.array(y_train)\n",
    "x_val_var = jt.array(x_val)\n",
    "y_val_var = jt.array(y_val)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.2 定义模型"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "模型的定义：我们定义模型需要继承 Jittor 的 Module 类，并实现  \\_\\_init__  函数和 execute 函数。\n",
    "* \\_\\_init__ 函数： 用于定义模型由哪些参数或操作组成； \n",
    "* execute 函数： 定义了模型执行的顺序和模型的返回值。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "模型 1\n",
    "\"\"\"\n",
    "from jittor import Module\n",
    "\n",
    "class FirstModel(Module):\n",
    "\n",
    "    def __init__(self):\n",
    "        super().__init__()\n",
    "\n",
    "        # 随机初始化参数 a 和 b\n",
    "        self.a = jt.rand(1)\n",
    "        self.b = jt.rand(1) \n",
    "\n",
    "    def execute(self, x):\n",
    "        # 模型通过输入的 x 值，进行与参数 a 和参数 b 的计算，得到预测的 y 值，并返回计算结果\n",
    "        y_pred = self.a + self.b * x\n",
    "        return y_pred"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来，用我们定义好的模型类，创建一个模型实例。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "model = FirstModel()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在，我们来瞧一眼这个实例模型，它初始化时随机的参数是多少："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'a': jt.Var([0.2822725], dtype=float32), 'b': jt.Var([0.15348685], dtype=float32)}\n"
     ]
    }
   ],
   "source": [
    "print(model.state_dict())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.3 定义损失函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们从 Jittor 的函数库里选择 MSE （均方误差）作为衡量 “模型输出值” 与 “正确标签” 差异大小的标准，即损失函数。  \n",
    "\n",
    "（提示： Jittor 内置的损失函数和优化器都在 nn 类中。您只需导入 nn 类，即可轻松地使用这些函数。）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入 nn 类\n",
    "from jittor import nn\n",
    "\n",
    "# 设置损失函数\n",
    "loss_function = nn.MSELoss()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.4 选择优化器"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们选择 Jittor 内置的 SGD （Stochastic Gradient Descent，随机梯度下降） 作为模型参数的优化器，并设置学习率 $learning\\_rate = 0.1$。  \n",
    "\n",
    "注意：在创建优化器实例的时候，我们需要将模型参数传入，代表我们的优化器将对这些参数进行优化更新。\n",
    "\n",
    "（提示：模型的参数可通过 model.parameters() 获取。）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 设置学习率\n",
    "learning_rate = 0.1\n",
    "\n",
    "# 传入模型参数，创建优化器实例\n",
    "optimizer = nn.SGD(model.parameters(), learning_rate)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.5 训练模型并验证"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先，我们完成模型训练的代码块："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train(model, x_train, y_train, loss_function, optimizer):\n",
    "    model.train()                                 # 开启训练模式\n",
    "    y_pred = model(x_train)                       # 将输入值 x_train 传入模型，计算得到输出值（即预测值） y_pred\n",
    "    loss = loss_function(y_train, y_pred)         # 通过损失函数，计算真实值 y_train 和预测值 y_pred 的差异大小\n",
    "    optimizer.step(loss)                          # 优化器根据计算出来的损失函数值对模型参数进行优化、更新\n",
    "    return loss                                  # 返回本次训练的 Loss 值，以便记录"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "随后，我们完成模型验证的代码块："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "def val(model, x_val, y_val, loss_function):\n",
    "    model.eval()                                  # 开启验证模式，不更新模型参数\n",
    "    y_pred = model(x_val)                         # 将输入值 x_val 传入模型，计算得到输出值（即预测值） y_pred\n",
    "    loss = loss_function(y_val, y_pred)           # 通过损失函数，计算真实值 y_val 和预测值 y_pred 的差异大小     \n",
    "    return loss                                  # 返回本次验证的 Loss 值，以便记录"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来，我们完成模型训练并验证的主代码块：  \n",
    "在执行前后，我们会分别打印出模型的参数，看我们的训练是否将模型参数训练成我们预期的 $a = 1$，$b = 2$。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Before training: \n",
      " {'a': jt.Var([0.2822725], dtype=float32), 'b': jt.Var([0.15348685], dtype=float32)}\n",
      "After training: \n",
      " {'a': jt.Var([0.9964309], dtype=float64), 'b': jt.Var([2.01100074], dtype=float64)}\n"
     ]
    }
   ],
   "source": [
    "# 打印训练前的模型参数\n",
    "print(\"Before training: \\n\", model.state_dict())\n",
    "\n",
    "# 设置迭代次数（在这个案例中，一个纪元（Epoch）即是一个迭代（Iteration））\n",
    "epochs = 500\n",
    "\n",
    "# 初始化空列表，分别用于记录训练集和验证集上的 Loss 值\n",
    "train_loss_list = list()\n",
    "val_loss_list = list()\n",
    "\n",
    "# 循环迭代训练\n",
    "for epoch in range(epochs):\n",
    "    # 在训练集上进行训练，将更新模型参数。\n",
    "    train_loss = train(model, x_train_var, y_train_var, loss_function, optimizer)\n",
    "    train_loss_list.append(train_loss)\n",
    "    # 在验证集上进行验证，模型参数不做更新。\n",
    "    val_loss = val(model, x_val_var, y_val_var, loss_function)\n",
    "    val_loss_list.append(val_loss.item())\n",
    "    \n",
    "# 打印训练结束后的模型参数\n",
    "print(\"After training: \\n\", model.state_dict())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从上述打印结果，可以看到，经过我们的训练，模型参数由最初的随机值朝着正确的方向发生了改变。  \n",
    "在训练结束后，我们的模型参数已明显的接近于 $a = 1$，$b = 2$。  \n",
    "这说明，我们的模型训练是成功的！"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.6 附：可视化验证"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最后，让我们利用可视化工具，验证一下实验结果吧。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Loss 在训练集和验证集上不同的下降趋势：**  \n",
    "* Loss 值越大，代表通过模型计算出的预测值 y_pred 和真实值 y 的差距越大；  \n",
    "* Loss 值越小，说明 y_pred 和 y 越来越接近，代表模型预测得越来越准确。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 作图前 50 个 Epoch 中 Loss 的变化趋势\n",
    "plt.plot(train_loss_list[:50],'r', label=\"Training Loss\")\n",
    "plt.plot(val_loss_list[:50],'g', label=\"Validation Loss\")\n",
    "plt.xlabel(\"Epochs\")\n",
    "plt.ylabel(\"Loss\")\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**比较验证集上 “原始数据点” 和 “模型预测直线” ：**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYsAAAEGCAYAAACUzrmNAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+j8jraAAAgAElEQVR4nO3df5zVc97/8cdLhqakUCyFshcj9GMyklIkW4S0llV7+RmXH5dycdlsXV9bVqxcWetqWbTr1/pVlsxG2aIQIU1Nv/RDUWiKUqayJpp6ff84Z6Zzzvw4M9N8zq953m+3bs15fz7nc159qnnO+/3+fN4fc3dERESqs0+yCxARkdSnsBARkbgUFiIiEpfCQkRE4lJYiIhIXPsmu4D61LJlS2/btm2yyxARSRvz58//xt1bxdsvo8Kibdu2FBQUJLsMEZG0YWaf12Q/DUOJiEhcCgsREYlLYSEiInFl1JxFZXbu3Mm6devYsWNHskuRetK4cWPatGlDVlZWsksRaTAyPizWrVtHs2bNaNu2LWaW7HJkL7k7mzdvZt26dbRr1y7Z5Yg0GBkfFjt27FBQZBAz45BDDmHTpk3JLkUk6fILixg3fSXri0s4okU2w/vlMDC3dSCflfFhASgoMoz+PkVCQTFy8hJKdu4CoKi4hJGTlwAEEhiBTXCbWWMz+8jMFpnZx2b2u0r22d/MJpnZajOba2ZtI7aNDLevNLN+QdUpIpKOxk1fWR4UZUp27mLc9JWBfF6QV0P9AJzl7p2AzsA5ZtYtZp9rgG/d/d+APwL3AZjZCcAg4ETgHODPZtYowFoDZWZcdtll5a9LS0tp1aoV559/fq2O07ZtW7755ps67dO2bVs6dOhAx44d6du3L1999VWtPjvSnXfeyf333w/AqFGjePPNN6vcd+HChUybNq389ZQpUxg7dmydP1tEQtYXl9SqfW8FFhYe8l34ZVb4V+yTli4Eng5//RLQx0JjDBcCE939B3dfA6wGugZVa9CaNm3K0qVLKSkJ/SW+8cYbtG4dzLhidd566y0WL15MXl4ev//976O2uTu7d++u9THvuusuzj777Cq3x4bFgAEDGDFiRK0/R0SiHdEiu1bteyvQ+yzMrJGZLQQ2Am+4+9yYXVoDXwK4eymwFTgksj1sXbitss+4zswKzKwglSc9+/fvz9SpUwF44YUXGDx4cPm2LVu2MHDgQDp27Ei3bt1YvHgxAJs3b6Zv376ceOKJXHvttUQ+1fDZZ5+la9eudO7cmeuvv55du6K7o9Xp1asXq1evZu3ateTk5HDFFVdw0kkn8eWXXzJu3DhOOeUUOnbsyOjRo8vfc88993Dcccdx+umns3Llnm7uVVddxUsvvQTAvHnz6N69O506daJr165s3bqVUaNGMWnSJDp37sykSZN46qmnGDp0KABr167lrLPOomPHjvTp04cvvvii/Jg333wz3bt355hjjik/voiE5BcW8a8fSiu0Z2c1Yni/nEA+M9AJbnffBXQ2sxbAK2Z2krsvrefPmABMAMjLy6v+GbG33AILF9bnx0PnzvDgg3F3GzRoEHfddRfnn38+ixcvZsiQIbz77rsAjB49mtzcXPLz85k1axZXXHEFCxcu5He/+x2nn346o0aNYurUqTz++OMALF++nEmTJjFnzhyysrL4z//8T5577jmuuOKKGpX82muv0aFDBwBWrVrF008/Tbdu3ZgxYwarVq3io48+wt0ZMGAAs2fPpmnTpkycOJGFCxdSWlpKly5dOPnkk6OO+eOPP3LppZcyadIkTjnlFLZt20aTJk246667KCgo4KGHHgLgqaeeKn/PsGHDuPLKK7nyyit54oknuPnmm8nPzwdgw4YNvPfee6xYsYIBAwZw8cUX1+jPJpLpYie2yxzUJIvRF5yY3ldDuXuxmb1FaP4hMiyKgCOBdWa2L9Ac2BzRXqZNuC1tdezYkbVr1/LCCy/Qv3//qG3vvfceL7/8MgBnnXUWmzdvZtu2bcyePZvJkycDcN5553HQQQcBMHPmTObPn88pp5wCQElJCYceemjcGnr37k2jRo3o2LEjd999N8XFxRx99NF06xaaSpoxYwYzZswgNzcXgO+++45Vq1axfft2fv7zn9OkSRMgNJQUa+XKlRx++OHlNR144IFx6/nggw/K/3yXX345t99+e/m2gQMHss8++3DCCSfw9ddfxz2WSENR2cQ2QJP99g0sKCDAsDCzVsDOcFBkAz8jPIEdYQpwJfABcDEwy93dzKYAz5vZA8ARwLHAR3tdVA16AEEaMGAAv/71r3n77bfZvHlznY/j7lx55ZXce++9tXrfW2+9RcuWLctfFxcX07Rp06jjjhw5kuuvvz7qfQ8m4bztv//+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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 利用训练好的模型，对验证集上的 x 进行计算，预测出 y_pred\n",
    "y_pred = model(x_val_var)  \n",
    "\n",
    "# Matplotlib 作图。注意，需将计图的 Var 类型转化为 NumPy 数组\n",
    "plt.scatter(x_val_var.numpy(), y_val_var.numpy(), label=\"Validation Data\")      # 原始数据点\n",
    "plt.plot(x_val_var.numpy(), y_pred.numpy(), 'r', label=\"Model Prediction\")      # 模型预测结果\n",
    "plt.xlabel(\"x\")\n",
    "plt.ylabel(\"y\")\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 📣\n",
    "恭喜您！已成功完成了线性回归的任务！🎉🎉🎉\n",
    "\n",
    "您可能觉得这个模型还太过简单、过于理论，无法应用到实际的神经网络训练中。  \n",
    "那么，请您继续最终章的挑战。  \n",
    "在终章中，我们会以上述模型为雏形，建立一个健全的神经网络，解决一个实际的分类问题。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}